package com.gf.framework.web.translate;

import com.gf.framework.common.model.IPage;
import com.gf.framework.common.result.R;
import com.gf.framework.web.annotation.Translator;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author gaofei
 * 结构翻译工具
 */
@Aspect
@Component
@Order(2)
@Slf4j
public class TranslatorAspect {
    @Resource
    private TranslateConvertFactory translateConvertFactory;
    @Resource
    private DefaultListableBeanFactory defaultListableBeanFactory;

    @SuppressWarnings("all")
    @AfterReturning(pointcut = "@annotation(com.gf.framework.web.annotation.Translator)", returning = "object")
    public void doAfter(JoinPoint joinPoint, Object object) {
        if (object == null) {
            return;
        }
        if (object instanceof R) {
            object = ((R<?>) object).getData();
        }
        if (object == null) {
            return;
        }

        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

            if (!isTranslator(methodSignature.getMethod().getAnnotation(Translator.class).condition(), joinPoint)) {
                return;
            }

            if (object instanceof Collection<?> || object instanceof IPage) {
                List<?> objects = object instanceof IPage ? ((IPage<?>) object).getRecords() :
                        new ArrayList<>((Collection<?>) object);
                if (CollectionUtils.isEmpty(objects)) {
                    return;
                }
                List<TranslateTypeConvert> converts = translateConvertFactory.getTranslateTypeConvert(objects.get(0).getClass());

                // 有定义好的转换器
                converts.forEach(convert -> {
                    try {
                        convert.convertCollection(objects);
                    }
                    catch (Exception e) {
                        log.error("翻译错误", e);
                    }
                });
            } else {
                Object finalObject = object;
                List<TranslateTypeConvert> converts = translateConvertFactory.getTranslateTypeConvert(finalObject.getClass());
                converts.forEach(convert -> {
                    try {
                        convert.convert(finalObject);
                    }catch (Exception e) {
                        log.error("翻译错误", e);
                    }
                });
            }
        }catch (Exception e) {
            log.error("翻译错误", e);
        }
    }

    /**
     * 条件判断，是否需要翻译
     * @param condition  条件
     * @param joinPoint 切入点
     * @return 是否需要翻译，false直接退出
     */
    private Boolean isTranslator(String condition, JoinPoint joinPoint) {
        if (!StringUtils.hasText(condition)) {
            return true;
        }

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        if (paraNameArr == null || paraNameArr.length == 0) {
            return true;
        }

        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        Expression expression = spelExpressionParser.parseExpression(condition);

        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], joinPoint.getArgs()[i]);
        }
        context.setBeanResolver(new BeanFactoryResolver(defaultListableBeanFactory));
        return expression.getValue(context, Boolean.class);
    }
}
